﻿//===============================================================================
// Microsoft Innovation Centre - Western Europe
// Copyright (c) 2008 Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace Microsoft.WVB.Framework.AdControls
{
    /// <summary>
    /// An <see cref="AdBase"/> derived control intended to display Images Ads.
    /// </summary>
    /// <remarks>When Templating this control, you will need to modify the AdImage.xaml file.
    /// this control has 2 controls part of its Template.
    /// A <see cref="Button"/> control named <b>CloseButton</b> used for Closing the Ad if allowed.
    /// A <see cref="Image"/> control named <b>MediaContent</b> for displaying the Ad creative.
    /// </remarks>
    [TemplatePart(Name = AdImage.CloseButtonName, Type = typeof(Button)),
    TemplatePart(Name = AdImage.MediaContentName, Type = typeof(Image))]
    public sealed class AdImage : AdBase, IDisposable
    {
        private const string CloseButtonName = "CloseButton";
        private const string MediaContentName = "MediaContent";
        private BitmapImage img = null;
        private int counter = 0;

        internal Button CloseButton = null;
        internal Image Media = null;

        private MouseButtonEventHandler adClickHandler = null;
        private RoutedEventHandler closeHandler = null;
        private EventHandler adTickHandler = null;
        private DispatcherTimer adTrackTimer = null;
        private OpenReadCompletedEventHandler openReadHandler = null;
        private WebClient wc = null;

        private System.Action closeAction = null;
        private System.Action playAction = null;

        /// <summary>
        /// The <see cref="AdImage"/> class constructor.
        /// </summary>
        /// <remarks>
        /// This is where we are defining the <see cref="Action"/> Start and Close actions for the media.
        /// </remarks>
        public AdImage()
            : base()
        {
            this.DefaultStyleKey = typeof(AdImage);

            wc = new WebClient();

            playAction = delegate()
            {
                if (Media != null)
                {
                    Media.Source = img;
                    if (adTrackTimer != null)
                    {
                        adTrackTimer.Start();
                    }
                }
            };

            closeAction = delegate()
            {
                if (Media != null)
                {
                    Media.Source = null;
                    if (adTrackTimer != null)
                    {
                        adTrackTimer.Stop();
                    }
                }
            };
        }

        /// <summary>
        /// Acquire the Control Template and the control instance mentioned in the Template Contract.
        /// AdImage has an <see cref="Image"/> and a <see cref="Button"/> control in its contract.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Media = base.GetTemplateChild(AdImage.MediaContentName) as Image;
            CloseButton = base.GetTemplateChild(AdImage.CloseButtonName) as Button;

            if (Media != null)
            {
                if (CloseButton != null)
                {
                    if (closeHandler == null)
                    {
                        closeHandler = delegate(object sender, RoutedEventArgs e)
                        {
                            OnAdStopped(this, new EventArgs());
                        };
                    }
                    CloseButton.Click += closeHandler;
                }
            }
        }

        /// <summary>
        /// Overriden from base class <see cref="BaseAd"/>. Executes all queued actions if any.
        /// </summary>
        /// <param name="oldValue">A <see cref="bool"/> representing the old value before the change.</param>
        /// <param name="newValue">A <see cref="bool"/> representing the new value after the change.</param>
        /// <remarks>Queued actions are present if the user interacts with the Ad placeholder even thought the Ad is not ready.
        /// i.e. the Video is not yet buffered but the user wants to Pause it or he wants to close it before it is displayed.</remarks>
        protected override void OnIsReadyChanged(bool oldValue, bool newValue)
        {
            if (QueuedActions.Count > 0)
            {
                for (int i = 0; i < QueuedActions.Count; i++)
                {
                    System.Action action = QueuedActions.Dequeue();
                    action.Invoke();

                    if (action == closeAction)
                        return;
                }
            }
        }

        /// <summary>
        /// Overriden from base class <see cref="BaseAd"/>. Checks Template and interprets information
        /// contained in the <see cref="AdContext"/> paramter. 
        /// </summary>
        /// <param name="adContext">A <see cref="AdContext"/> object containing all the information about the Ad.</param>
        public override void Buffer(AdContext adContext)
        {
            if ((Media != null) && (CloseButton != null))
            {
                if (CloseButton != null)
                {
                    CloseButton.IsEnabled = adContext.CanSkip;
                }

                if (adContext.Duration != TimeSpan.Zero)
                {
                    adTrackTimer = new DispatcherTimer();

                    TimeSpan adHalfTime = TimeSpan.FromSeconds((adContext.Duration.TotalSeconds / 2));
                    adTrackTimer.Interval = adHalfTime;

                    if (adTickHandler == null)
                    {
                        adTickHandler = delegate(object sender, EventArgs e)
                        {
                            if (counter == 0)
                            {
                                this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.Middle));
                                counter++;
                            }
                            else if (counter == 1)
                            {
                                this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.End));
                                this.OnAdEnded(this, new EventArgs());
                                counter++;
                            }
                            else
                            {
                                adTrackTimer.Tick -= adTickHandler;
                                adTrackTimer.Stop();
                            }
                        };
                    }
                    adTrackTimer.Tick += adTickHandler;
                }
                else
                {
                    this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.Beginning));
                }

                if (adClickHandler == null)
                {
                    adClickHandler = delegate(object sender, MouseButtonEventArgs e)
                    {
                        System.Windows.Browser.HtmlPage.Window.Navigate(adContext.Click, "_blank");
                    };
                }
                Media.MouseLeftButtonUp += adClickHandler;

                if (openReadHandler == null)
                {
                    openReadHandler = delegate(object sender, OpenReadCompletedEventArgs e)
                    {
                        Stream s = e.Result;
                        img = new BitmapImage();
                        img.SetSource(s);

                        wc.OpenReadCompleted -= openReadHandler;
                        this.IsReady = true;
                    };
                    wc.OpenReadCompleted += openReadHandler;
                }
                                
                wc.OpenReadAsync(adContext.Source);
            }
        }

        /// <summary>
        /// Overriden. Starts the <see cref="Action"/> defined as a starting Action in the constructor.
        /// </summary>
        public override void Start()
        {
            this.OnAdImpressionTracked(this, new AdTrackedEventArgs(AdImpressionTiming.Beginning));
            if (this.IsReady)
                playAction.Invoke();
            else
            {
                QueuedActions.Enqueue(playAction);
            }
        }

        /// <summary>
        /// Overriden. Executes the <see cref="Action"/> defined as the Stop action 
        /// (when a user explicitly stop the Ad). 
        /// </summary>
        /// <param name="sender">The <see cref="Object"/> which fired the event.</param>
        /// <param name="e">An <see cref="EventArgs"/> parameter.</param>
        protected override void OnAdStopped(object sender, EventArgs e)
        {
            if (this.IsReady)
                closeAction.Invoke();
            else
            {
                QueuedActions.Enqueue(closeAction);
            }

            base.OnAdStopped(sender, e);
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (CloseButton != null)
            {
                CloseButton.Click -= closeHandler;
            }

            if (adTrackTimer != null)
            {
                adTrackTimer.Tick -= adTickHandler;
            }

            if (Media != null)
            {
                Media.MouseLeftButtonUp -= adClickHandler;
            }

            if (wc != null)
            {
                wc.OpenReadCompleted -= openReadHandler;
            }
        }

        #endregion
    }
}
